#include <iostream>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <thread>

using namespace std;

class H2O
{
    condition_variable readyRelease;
    condition_variable waitRelease;
    int hCount = 0;
    int oCount = 0;
    int hRelease = 0;
    int oRelease = 0;
    mutex mtx;

public:
    H2O()
    {
    }

    void hydrogen(function<void()> releaseHydrogen)
    {
        {
            unique_lock<mutex> lock(mtx);
            readyRelease.wait(lock, [this]
                              { return hCount < 2; });
            hCount++;
        }
        releaseHydrogen();
        {
            lock_guard<mutex> lock(mtx);
            hRelease++;
            if (hRelease == 2 && oRelease == 1)
            {
                hRelease = 0;
                oRelease = 0;
                hCount = 0;
                oCount = 0;
                readyRelease.notify_all();
            }
        }
    }

    void oxygen(function<void()> releaseOxygen)
    {
        {
            unique_lock<mutex> lock(mtx);
            readyRelease.wait(lock, [this]
                              { return oCount < 1; });
            oCount++;
        }
        releaseOxygen();
        {
            lock_guard<mutex> lock(mtx);
            oRelease++;
            if (hRelease == 2 && oRelease == 1)
            {
                hRelease = 0;
                oRelease = 0;
                hCount = 0;
                oCount = 0;
                readyRelease.notify_all();
            }
        }
    }
};

int main()
{
    cout << "Code1117" << endl;
    H2O *h2o = new H2O();
    function<void()> h = []()
    { cout << 'H'; };
    function<void()> o = []()
    { cout << 'O'; };
    thread t1([&]()
              { h2o->hydrogen(h); });
    thread t2([&]()
              { h2o->oxygen(o); });
    thread t3([&]()
              { h2o->hydrogen(h); });
    thread t4([&]()
              { h2o->oxygen(o); });
    thread t5([&]()
              { h2o->hydrogen(h); });
    thread t6([&]()
              { h2o->hydrogen(h); });
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    t6.join();
    return 0;
}